<?php

namespace App\Command\ResumeParsing\Service;

use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;

class DataStructAdapter {
    /**
     * @var string
     */
    protected $data;
    /**
     * @var \DOMDocument
     */
    protected $dom;
    /**
     * @var bool
     */
    public $isValid;
    /**
     * @var \DOMXPath
     */
    protected $xPath;
    /**
     * @var Worksheet
     */
    protected $sheet;
    /**
     * @var array
     */
    protected $ignoreTagName;
    /**
     * @var array
     */
    protected $paragraphTagName;
    /**
     * @var array
     */
    protected $plainTagName;

    public function __construct($data) {
        $this->legacyData = $data['resumeAtom'] ?? '';
        //$this->isValid = !empty($this->legacyData);
        //return;

        $this->func = new ParsingFunction();
        $this->isValid = false;
        if (!empty($data['resumeSheet'])) {
            if ($data['resumeSheet'] instanceof Worksheet) {
                $this->type = 'excel';
                $this->sheet = $data['resumeSheet'];
                $this->isValid = true;
            }
        } elseif (is_null($data['resumeDom'])) {
            $this->type = '';
            $this->isValid = true;
        } elseif (!empty($data['resumeBase'])) {
            $this->data = $data['resumeBase'];
            $this->dom = new \DOMDocument();
            if (preg_match('/<w:(?:wordD|d)ocument /', substr($this->data, 0, 200))) {
                $this->type = 'docx';
                $this->isValid = @$this->dom->loadXML($this->data, LIBXML_NOENT | LIBXML_XINCLUDE | LIBXML_NOERROR | LIBXML_NOWARNING);
            }
            if (!$this->isValid) {
                $this->type = 'html';
                $this->isValid = @$this->dom->loadHTMLFile($data['filePath']);
            }
            if (!$this->isValid) {
                $this->type = 'html';
                $this->isValid = @$this->dom->loadHTML($this->data);
            }
            if ($this->isValid) {
                $this->xPath = new \DOMXPath($this->dom);
            }
        }
    }

    /**
     * @return string
     */
    public function getPlainText() {
        switch ($this->type) {
            case 'excel':
//                $arr = $this->sheet->toArray();
                //只保留前三列，去掉简历英文部分（目前格式中前三列为有效数据，如出现其他格式的再进行调整）
                $maxRow = $this->sheet->getHighestRow();
                $result = '';
                $blankCellNum = 0;
                for ($row=1;$row<=$maxRow;$row++) {
                    $tmpArr = [];
                    for ($col='A';$col<='C';$col++) {
                        $data = $this->sheet->getCell($col.$row)->getValue();
                        //当单元格数据为空，记录
                        if (empty($data) || is_null($data)) {
                            $blankCellNum++;
                            if ($blankCellNum == 15) {//当连续单元格为空达到15个，跳出整个循环体，避免因最大行数太大导致的性能问题
                                break 2;
                            }
                            continue;
                        } else {//单元格不为空的情况
                            $blankCellNum = 0;
                            $tmpArr = array_merge($tmpArr,[$data."\r\n"]);
                        }
                    }
                    //todo:excel方式读取时的身份证时为科学计数法，暂不将其存入数据库中
                    if (!empty($tmpArr) && preg_match('/(.*)身份证(.*)/',$tmpArr[0])) {
                        $tmpArr[1] = '';
                    }
                    $result .= implode('',$tmpArr);
                }
//                $result = implode("\r\n", array_filter(array_map(function (array $row) {
//                    return implode("\r\n", array_filter($row));
//                }, $arr)));
                return $result;
            case 'docx':
                //https://blog.csdn.net/weixin_34332905/article/details/94735947?tdsourcetag=s_pctim_aiomsg节点参考
                $this->ignoreTagName = ['w:pPr' => true, 'w:rPr' => true, 'w:tblPr' => true, 'w:trPr' => true, 'w:tcPr' => true, 'w:sectPr' => true,'wp:posOffset' => true,
                    'o:DocumentProperties' => true, 'w:fonts' => true, 'w:styles' => true, 'w:pict' => true];
                $this->paragraphTagName = ['w:p' => true,'w:br' => true];//,'w:r' => true, 'w:tc' => true
                $this->plainTagName = ['w:t' => true];
                return $this->domToText($this->dom->documentElement);
            case 'html':
                $this->ignoreTagName = ['style' => true];
                $this->paragraphTagName = ['p' => true, 'br' => true, 'td' => true, 'h1' => true, 'h2' => true, 'h3' => true, 'h4' => true, 'h5' => true, 'h6' => true];
                $this->plainTagName = [];
                return $this->domToText($this->dom->documentElement);
            default:
                return preg_replace('/[\x10]+/u', "\r\n", $this->legacyData);
        }
    }

    private function inspectNode(\DOMElement $domElement) {
        $dataNode = [];
        $dataNode['tagName'] = $domElement->tagName;

        $result = $this->xPath->query('*', $domElement);
        if (!count($result)) return $dataNode;

        $dataNode['childNodes'] = [];
        foreach ($result as $item) {
            $dataNode['childNodes'][] = $this->inspectNode($item);
        }
        return $dataNode;
    }

    private function domToText(\DOMNode $node) {
        if ($node instanceof \DOMText) return preg_replace('/\s+/u', ' ', trim($node->wholeText));
        if (!$node instanceof \DOMElement) return '';
        if (isset($this->ignoreTagName[$node->tagName])) return '';
        //$this->tagNameDict[$node->tagName] = ($this->tagNameDict[$node->tagName] ?? 0) + 1;
        $result = isset($this->paragraphTagName[$node->tagName]) ? "\r\n" : '';
        if (isset($this->plainTagName[$node->tagName]) || !$node->hasChildNodes()) return $result . $node->textContent;
        foreach ($node->childNodes as $childNode) {
            $result .= $this->domToText($childNode);
        }
        //word文档换行符丢失问题
//        if (isset($this->paragraphTagName[$node->tagName]) && substr($result, 2, 2) == "\r\n") $result = substr($result, 2);

        return $result;
    }

    public function getStructuredData() {
        return $this->domToText($this->dom->documentElement);
    }



    //调试用，打印指定XPath相关信息
    //autoFormat 1 - 普通；2 - 智能去除换行，3 - 所有换行替换为空格，4 - 去除全部换行，否则不处理
    protected function listXPath($queryPath, $autoFormat = -1, $exclude = array('html', 'body', 'document')) {
        echo "--------------------XPath Debugger【" . $queryPath . "】--------------------\r\n";
        $result = $this->xPath->query($queryPath);
        if (!$result || $result->length < 1) {
            var_dump($queryPath . "\treturns no results!");
            exit;
        }
        $resultCount = 0;
        foreach ($result as $node) {
            if ((!isset($node->tagName) && empty(trim($node->textContent))) || (isset($node->tagName) && in_array($node->tagName, $exclude))) continue;
            $resultCount++;
            $this->renderNode($node, $autoFormat);
        }
        if ($resultCount == 0) {
            if ($this->renderNode($result, $autoFormat) == 0) {
                var_dump($queryPath . "\treturns only empty results!");
            }
        }
        exit;
    }

    //调试用，打印指定DOMNode（或DOMNode集合）相关信息
    //autoFormat 1 - 普通；2 - 智能去除换行，3 - 所有换行替换为空格，4 - 去除全部换行，否则不处理
    protected function renderNode($node, $autoFormat = -1, $renderAttr = array('class', 'style', 'w:val', 'w:eastasia', 'w:cs')) {
        if (is_iterable($node)) {
            $resultCount = 0;
            foreach ($node as $child) {
                if (!$child instanceof \DOMNode || (!isset($child->tagName) && empty(trim($child->textContent)))) continue;
                $resultCount++;
                $this->renderNode($child, $autoFormat);
            }
            return $resultCount;
        }
        if (!$node instanceof \DOMNode) {
            var_dump('Not a DOMNode: ', $node);
            return;
        }
        $xPath = $node->getNodePath();
        var_dump($xPath);
        if (($xPathSimple = preg_replace('/\[\d+\]/', '', $xPath)) != $xPath) var_dump($xPathSimple);
        if (isset($node->attributes)) {
            foreach ($renderAttr as $attrName) {
                $attr = $node->attributes->getNamedItem($attrName);
                if ($attr && !is_null($attrValue = $attr->value)) {
                    var_dump($attrName . ":\t" . $attrValue);
                }
            }
        }
        //if (is_numeric($autoFormat) && $autoFormat > 0 && mb_strpos($node->textContent, $this->br) !== FALSE) $autoFormat = 4;
        switch ($autoFormat) {
            case 1:
                $content = $this->func->formatTxt($node->textContent);
                break;
            case 2:
                $content = $this->func->formatTxt($node->textContent, 'auto');
                break;
            case 3:
                $content = $this->func->formatTxt($node->textContent, 'space');
                break;
            case 4:
                $content = $this->func->formatTxt($node->textContent, TRUE);
                break;
            case 0:
                $content = $node->textContent;
                break;
            default:
                $content = $this->func->formatTxt($node->textContent);
        }
        if (is_numeric($autoFormat) && $autoFormat > 0) $content = preg_replace('/' . preg_quote($this->br) . '/u', "\r\n", $content);
        if (!empty($content)) var_dump($content);
        echo "-------------------------------------------------------------------------------------------------------------------------------------\r\n";
    }
}